﻿/*                       NOTICE
 * 
 * This software was produced for the U. S. Government
 * under Contract No. FA8721-10-C-0001, and is
 * subject to the Rights in Noncommercial Computer Software
 * and Noncommercial Computer Software Documentation Clause
 * (DFARS) 252.227-7014 (JUN 1995)
 * 
 * Approved for Public Release: 11-2435
 * © 2011 The MITRE Corporation. All Rights Reserved. 
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using TUIO;

namespace GRIP.Gestures
{
    public abstract class BasicGesture : Gesture
    {
        // Couple basic variables
        protected ComplexGesture parent;
        public Vector3 StartPosition = new Vector3();
        public Vector3 LastPosition = new Vector3();
        public Vector3 CurrentPosition = new Vector3();
        public Vector3 Speed = new Vector3();
        public GestureCardinality Cardinality = GestureCardinality.Generic;
        protected GestureEvent<Gesture> CurrentEvent;
        protected bool phantom;

        public BasicGesture(DateTime time, long id, string source, TuioContact contact, GestureCardinality cardinality)
            : base(time, id, source)
        {
            ID = id;
            Cardinality = cardinality;
            StartTime = time;
            StartPosition.X = LastPosition.X = CurrentPosition.X = contact.xpos;
            StartPosition.Y = LastPosition.Y = CurrentPosition.Y = contact.ypos;
            StartPosition.Z = LastPosition.Z = CurrentPosition.Z = contact.zpos;
            Speed.X = contact.getXSpeed();
            Speed.Y = contact.getYSpeed();
            Speed.Z = contact.getZSpeed();
        }
        
        public virtual void UpdatePosition(TuioContact contact)
        {
            CurrentPosition.CopyTo(LastPosition);
            CurrentPosition.X = contact.xpos;
            CurrentPosition.Y = contact.ypos;
            CurrentPosition.Z = contact.zpos;
            Speed.X = contact.getXSpeed();
            Speed.Y = contact.getYSpeed();
            Speed.Z = contact.getZSpeed();
        }

        public abstract void Update(TuioContact contact);
        public abstract void Destroy(DateTime time);
        public abstract void SetParent(ComplexGesture cg);
        public abstract void ParentDestroyed();
    }

    public abstract class BasicGesture<T> : BasicGesture where T : BasicGesture<T>
    {
        public static event EventHandler<GestureEventArgs<T>> Added;
        public static event EventHandler<GestureEventArgs<T>> Changed;
        public static event EventHandler<GestureEventArgs<T>> Removed;
        public static List<T> ActiveGestures = new List<T>();

        public static void AddListeners(
            Action<object, GestureEventArgs<T>> OnAdded,
            Action<object, GestureEventArgs<T>> OnChanged,
            Action<object, GestureEventArgs<T>> OnRemoved)
        {
            Added += new EventHandler<GestureEventArgs<T>>(OnAdded);
            Changed += new EventHandler<GestureEventArgs<T>>(OnChanged);
            Removed += new EventHandler<GestureEventArgs<T>>(OnRemoved);
        }

        public BasicGesture(DateTime time, long id, string source, TuioContact contact, GestureCardinality cardinality)
            : base(time, id, source, contact, cardinality)
        {
            CurrentEvent = new GestureEvent<Gesture>(this, GestureEventType.Added);
            Recognizer.PendingEvents.Add(CurrentEvent);
        }

        public override void Update(TuioContact contact)
        {
            UpdatePosition(contact);
            if (!phantom && (parent == null || !parent.Update()))
            {
                CurrentEvent = new GestureEvent<Gesture>(this, GestureEventType.Changed);
                Recognizer.PendingEvents.Add(CurrentEvent);
            }
        }

        public override void Destroy(DateTime time)
        {
            this.EndTime = time;
            if (!phantom && (parent == null || !parent.ChildRemoved(this)))
            {
                if (CurrentEvent == null)
                    Recognizer.PendingEvents.Add(new GestureEvent<Gesture>(this, GestureEventType.Removed));
                else
                    Recognizer.PendingEvents.Remove(CurrentEvent);
            }
        }

        public override void OnAdded(ContactFrameReadyEventArgs Frame)
        {
            T gesture = (T)this;
            ActiveGestures.Add(gesture);
            CurrentEvent = null;
            if (Added != null)
                Added(Frame.AdapterSource, new GestureEventArgs<T>(gesture, Frame.FrameNumber));
        }

        public override void OnChanged(ContactFrameReadyEventArgs Frame)
        {
            CurrentEvent = null;
            if(Changed != null)
                Changed(Frame.AdapterSource, new GestureEventArgs<T>((T)this, Frame.FrameNumber));
        }

        public override void OnRemoved(ContactFrameReadyEventArgs Frame)
        {
            T gesture = (T)this;
            CurrentEvent = null;
            if(parent == null)
                ActiveGestures.Remove(gesture);
            if (Removed != null)
                Removed(Frame.AdapterSource, new GestureEventArgs<T>(gesture, Frame.FrameNumber));
        }

        public override void SetParent(ComplexGesture cg)
        {
            parent = cg;
            if (CurrentEvent == null)
                Recognizer.PendingEvents.Add(new GestureEvent<Gesture>(this, GestureEventType.Removed));
            else if (CurrentEvent.EventType == GestureEventType.Added)
                Recognizer.PendingEvents.Remove(CurrentEvent);
            else if (CurrentEvent.EventType == GestureEventType.Changed)
            {
                Recognizer.PendingEvents.Remove(CurrentEvent);
                Recognizer.PendingEvents.Add(new GestureEvent<Gesture>(this, GestureEventType.Removed));
            }
        }

        public override void ParentDestroyed()
        {
            parent = null;
            phantom = true;
            ActiveGestures.Remove((T)this);
        }

        public override XmlElement ToXML()
        {
            XmlDocument doc = new XmlDocument();
            XmlElement element = doc.CreateElement(((T)this).ToString());
            element.SetAttribute("ID", ID.ToString());
            element.SetAttribute("StartTime", StartTime.ToString());
            element.SetAttribute("Source", Source);
            element.SetAttribute("Cardinality", Cardinality.ToString());

            element.AppendChild(doc.ImportNode(CurrentPosition.ToXml("CurrentPosition"), false));
            element.AppendChild(doc.ImportNode(LastPosition.ToXml("LastPosition"), false));
            element.AppendChild(doc.ImportNode(Speed.ToXml("Speed"), false));
            return element;
        }
    }
}
